package fybug.nulll.pdstream.WR.Parsing.Source;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import org.jetbrains.annotations.Contract;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackReader;
import java.io.Reader;
import java.io.StringReader;

import fybug.nulll.pdfunctionlibrary.Annotations.MaybeStop;
import fybug.nulll.pdfunctionlibrary.Processing.Check;
import fybug.nulll.pdfunctionlibrary.Processing.Err.CallStop;
import fybug.nulll.pdfunctionlibrary.Processing.Err.Stop;
import fybug.nulll.pdstream.WR.Parsing.Source.Partition.ParsingPartition;

import static fybug.nulll.pdstream.StreamOperator.toBufferStream;
/**
 * <h2>文本解析源.</h2>
 * <pre>
 * 用于在 <b>解析文本</b> 的时候
 * 根据 <b>分割器</b> 分割出 <b>要解析的部分</b>
 * 抛出 <b>分割后的文本</b> 给上层处理
 * </pre>
 *
 * @author fybug
 * @version 0.1.1
 * @see ParsingPartition
 * @see ParsingAbstractSource
 * @see ParsingSourceS
 * @since JDK 1.8
 */
@SuppressWarnings( "all" )
public
class ParsingSource extends ParsingAbstractSource {
    // 分割器
    @Nullable private ParsingPartition[] partition = new ParsingPartition[1];
    // 数据源
    @Nullable private PushbackReader bufferRead;
    private Reader reader = null;
    // 是否可以读取
    private volatile boolean canread = false;
    // 同步锁
    @NotNull private final Object partitionLock = new Object(), bufferReadLock = new Object();

    @Override
    public
    int hashCode()
    {
        return ((super.hashCode() + partitionLock.hashCode()) << 5 + bufferReadLock.hashCode()) <<
               5;
    }

    /**
     * <p>使用字节流作为来源构建解析源.</p>
     * <p>并包装为缓冲流</p>
     */
    public
    ParsingSource(@NotNull final InputStream reader, @Nullable final ParsingPartition partition)
    { this(toBufferStream(new InputStreamReader(reader), 8192), partition); }

    /** <p>使用字符流作为来源构建解析源.</p> */
    public
    ParsingSource(@NotNull final Reader reader, @Nullable final ParsingPartition p)
    { binPartition(p).binreader(reader); }

    /** <p>使用字符串作为来源构建解析源.</p> */
    public
    ParsingSource(@NotNull final CharSequence reader, @Nullable final ParsingPartition partition)
    { this(new StringReader(String.valueOf(reader)), partition); }

    @Override
    protected
    boolean consistent(@NotNull final Object obj) {
        @NotNull ParsingSource soure = (ParsingSource) obj;
        boolean b;
        // 检查分隔符
        b = Check.equalsArrayTable(partition, soure.partition);
        // 检查流
        b &= Check.equalsFidle(bufferRead, soure.bufferRead);
        return b;
    }

    /*
     * Partition
     */

    @NotNull
    @Override
    public final
    ParsingSource binPartition(@Nullable final ParsingPartition p) {
        try {
            synchronized ( partitionLock ){
                Check.checkNull(partition)[0] = p;
                binreader(reader);
            }
        } catch ( Stop ignored ) {
        }
        return this;
    }

    @NotNull
    @Override
    @MaybeStop
    public final
    ParsingPartition getPartition() { return Check.checkArrayTable(partition); }

    /*
     * Reader
     */

    /** <p>绑定读取流.</p> */
    @NotNull
    public final
    ParsingSource binreader(@NotNull final Reader r) {
        if (r == null)
            return this;
        int pushbuffer; // 根据标识符长度创建回退区
        int buffer; // 根据最大读取长度创建缓冲区
        synchronized ( bufferReadLock ){
            bufferRead = null;
            reader = r;
            try {
                @NotNull final ParsingPartition p = getPartition();
                buffer = p.size();
                pushbuffer = p.lenght();
                // 检查缓冲区大小
                if (buffer <= 0) {
                    buffer = pushbuffer;
                    if (buffer <= 0)
                        buffer = 1024;
                }
                // 检查回推区大小
                if (pushbuffer <= 0)
                    pushbuffer = 1;
            } catch ( Stop e ) {
                /* 默认值 */
                buffer = 1024;
                pushbuffer = 1;
            }
            this.bufferRead = new PushbackReader(new BufferedReader(reader, buffer), pushbuffer);
        }
        canread = true;
        return this;
    }

    /*
     * 处理
     */

    @Contract( pure = true )
    @Override
    protected
    void rollback(final int size, @NotNull final StringBuilder stringBuilder) {
        try {
            // 回推对应的数据
            bufferRead.unread(stringBuilder.substring(stringBuilder.length() - size).toCharArray());
        } catch ( IOException | NullPointerException ignored ) {
        }
    }

    @MaybeStop
    @Override
    protected
    int get()
    {
        int i; // 读取出的单个字符
        try {
            i = bufferRead.read();
        } catch ( IOException | NullPointerException e ) {
            // 读取失败,发出中断信号
            canread = false;
            throw CallStop.INSTANCE.getInstance();
        }
        if (i == -1)
            canread = false;
        return i;
    }

    /*
     * Check
     */

    @Override
    public
    boolean readly() { return canread; }

    /*
     * 释放
     */

    @Override
    public
    void clean() {
        super.clean();
        binPartition(null);
        synchronized ( bufferReadLock ){
            bufferRead = null;
            reader = null;
        }
    }

    @Override
    public
    void free() {
        try {
            @Nullable final PushbackReader reader = Check.checkNull(this.bufferRead);
            synchronized ( reader ){
                // 关闭流
                reader.close();
            }
        } catch ( Throwable ignored ) {
        }
        clean();
    }

    @Override
    public
    void close() {
        synchronized ( partitionLock ){
            try {
                Check.checkArrayTable(this.partition).close();
            } catch ( Stop ignored ) {
            }
            this.partition = null;
        }
        free();
        super.close();
    }
}
